/*****************************************************************************************

    Author  ： Jack.Pan
    Date    ： 2021/9/14
    Desc    ： Virtual FIB Slave module for YSYX210152_Simulation
    Version ：0.0 (Original)

Version0.0 : support FIB command : SIGR, SIGW, SEQR w. BURST=3 SIZE=8, SEQW w. BURST=3 SIZE=8

*****************************************************************************************/
`include "PRV564Config.v"
`include "PRV564Define.v"
module Virtual_FIB_Slave(
    input  wire             		CLKi,
    input  wire             		RSTi,
//--------------------FIB interface----------------
    input  wire                     VFIBSi_REQ,         //Request for using bus
    output wire             		VFIBSo_ACK,         //ack for valid (read a new access from FIFO)
    output wire                     VFIBSo_FULL,
    input  wire             		VFIBSi_WREN,        //Write to FIFO enable
    input  wire [7:0]       		VFIBSi_ID,
    input  wire [7:0]       		VFIBSi_CMD,
    input  wire [3:0]       		VFIBSi_BURST,
    input  wire [3:0]       		VFIBSi_SIZE,
    input  wire [`XLEN-1:0] 		VFIBSi_ADDR,      
    input  wire [`XLEN-1:0] 		VFIBSi_DATA,
    //            reply to master
    output  reg [7:0]       		VFIBSo_ID,
    output  reg [7:0]       		VFIBSo_RPL,			//应答
    output  reg             		VFIBSo_V,
//  output  reg             		FIBo_ADDR,
    output  reg [`XLEN-1:0] 		VFIBSo_DATA,
//----------------SRAM interface-----------------------
// 注意！这个SRAM是异步读出的，因此仅适用于仿真
    output  reg                     SRAM_WREN,
    output  reg [7:0]               SRAM_BSEL,
    output  reg [`XLEN-1:0]         SRAM_ADDR,
    output  reg [`XLEN-1:0]         SRAM_DATAi,
    input  wire [`XLEN-1:0]         SRAM_DATAo

);
    localparam  FIB_IDLE     = 4'h0,        //空闲
                FIB_SIG_READ = 4'h2,        //单次读
                FIB_SIG_WRITE= 4'h4,        //单次写
                FIB_SEQ_WRITE= 4'h6,        //连续写
                FIB_SEQW_NEXT= 4'h7,        //读下一个表项
                FIB_SEQ_READ = 4'h8,        //连续读
                FIB_READ_NEW = 4'ha;        //读下一个表项

assign VFIBSo_ACK = VFIBSi_REQ;
//-------------------FIB Access Queue out(from FIFO)-------------------
    wire                 FIB_AQ_V;
    wire [7:0]           FIB_AQ_ID;
    wire [7:0]           FIB_AQ_CMD;
    wire [3:0]           FIB_AQ_BURST;
    wire [3:0]           FIB_AQ_SIZE;
    wire [`XLEN-1:0]     FIB_AQ_ADDR;
    wire [`XLEN-1:0]     FIB_AQ_DATA;
    wire                 FIB_AQ_ACk;                        //从AQ FIFO中读取新的一项目
    reg [3:0]            FIB_FSM;
    reg [7:0]            Burst_cnt;                         //Burst Number count
//----------------------字节选择解码-------------------------------------------
    wire [7:0]           Byte_sel;
DataShiftL      DataShiftL(
    .Offset_ADDR            ({1'b0,FIB_AQ_ADDR[2:0]}),
    .DATAi                  (64'h0),
    .SIZEi                  (FIB_AQ_SIZE),
    .MisAligned             (),         //当前访问不对齐
    .BSELo                  (Byte_sel),
    .DATAo                  ()
);

//--------------------------------FIB Access Queue FIFO------------------------------------
//         此FIFO用于模拟在实际情况中FIB总线桥与FIB主机之间连接的FIFO
SyncFIFO #(
.DATA_WITH                      (152)
)FIB_AQ_FIFO(
//----------------Global Sognal--------------------
	.FIFOi_CLK                  (CLKi),
	.FIFOi_RST                  (RSTi),
//---------------FIFO write ports--------------------	
	.FIFOi_WEN                  (VFIBSi_WREN),
	.FIFOi_DATA                 ({VFIBSi_ID, VFIBSi_CMD, VFIBSi_BURST, VFIBSi_SIZE, VFIBSi_DATA, VFIBSi_ADDR}),
	.FIFOo_FULL                 (VFIBSo_FULL),		
//---------------FIFO read ports--------------------	
	.FIFOi_REN                  (FIB_AQ_ACk),				//读出请求
	.FIFOo_valid                (FIB_AQ_V),
	.FIFOo_DATA                 ({FIB_AQ_ID, FIB_AQ_CMD, FIB_AQ_BURST, FIB_AQ_SIZE, FIB_AQ_DATA, FIB_AQ_ADDR})
);
always@(posedge CLKi or posedge RSTi)begin
    if(RSTi)begin
        FIB_FSM <= FIB_IDLE;
    end
    else begin
        case(FIB_FSM)
            FIB_IDLE     : if(FIB_AQ_V)begin
                            case(FIB_AQ_CMD)
                                `FIB_CMD_NOOP : begin 
                                                    FIB_FSM <= FIB_READ_NEW;
                                                    $display("Current FIB command is NOOP");
                                                end
                                `FIB_CMD_SIGR : begin 
                                                    FIB_FSM <= FIB_SIG_READ;
                                                    $display("Current FIB command is SIGR, ID=%h, ADDR=%h",FIB_AQ_ID, FIB_AQ_ADDR);
                                                end
                                `FIB_CMD_SIGW : begin 
                                                    FIB_FSM <= FIB_SIG_WRITE;
                                                    $display("Current FIB command is SIGW, ID=%h ADDR=%h",FIB_AQ_ID, FIB_AQ_ADDR);
                                                end
                                `FIB_CMD_SEQR : if((FIB_AQ_BURST==4'h3)&(FIB_AQ_SIZE==4'h3))begin 
                                                    FIB_FSM <= FIB_SEQ_READ;
                                                    $display("Current FIB command is SEQR, ID=%h, BURST=3, SIZE=3, Base ADDR=%h",FIB_AQ_ID, FIB_AQ_ADDR);
                                                end
                                                else begin 
                                                    FIB_FSM <= FIB_READ_NEW;
                                                    $display("Unsupport FIB SEQR length, only support BURST=3 and SIZE=3, command BURST=%h SIZE=%h", FIB_AQ_BURST, FIB_AQ_SIZE);
                                                end
                                `FIB_CMD_SEQW : if((FIB_AQ_BURST==4'h3)&(FIB_AQ_SIZE==4'h3))begin 
                                                    FIB_FSM <= FIB_SEQ_WRITE;
                                                    $display("Current FIB command is SEQW, ID=%h, BURST=3, SIZE=3, Base ADDR=%h",FIB_AQ_ID, FIB_AQ_ADDR);
                                                end
                                                else begin
                                                    FIB_FSM <= FIB_READ_NEW;
                                                    $display("Unsupport FIB SEQW length, only support BURST=3 and SIZE=3, command BURST=%h SIZE=%h", FIB_AQ_BURST, FIB_AQ_SIZE);
                                                end
                                `FIB_CMD_SEQE : begin 
                                                    FIB_FSM <= FIB_READ_NEW;
                                                    $display("Unexpected FIB command: SEQE");
                                                end
                                default       : begin 
                                                    FIB_FSM <= FIB_READ_NEW;
                                                    $display("Unsupport FIB command %h", FIB_AQ_CMD);
                                                end
                            endcase 
                           end
            FIB_SIG_READ : begin FIB_FSM <= FIB_READ_NEW; $display("Read Data = %h", SRAM_DATAo); end
            FIB_SIG_WRITE: begin FIB_FSM <= FIB_READ_NEW; $display("Burst count = %h, Write Data = %h", Burst_cnt, FIB_AQ_DATA); end
            FIB_SEQ_READ : begin FIB_FSM <= (Burst_cnt==8'h7) ? FIB_READ_NEW : FIB_SEQ_READ; $display("Burst count = %h, Read Data = %h", Burst_cnt, SRAM_DATAo); end
            FIB_SEQ_WRITE: begin FIB_FSM <= (Burst_cnt==8'h7) ? FIB_READ_NEW : FIB_SEQW_NEXT; $display("Burst count = %h, Write Data = %h", Burst_cnt, FIB_AQ_DATA); end  //如果Burst长度还不到指定数，则继续读下一条
            FIB_SEQW_NEXT: begin FIB_FSM <= FIB_SEQ_WRITE; end   
            FIB_READ_NEW : begin FIB_FSM <= FIB_IDLE; end
            default      : begin FIB_FSM <= FIB_IDLE; end
        endcase
    end
end
//----------------------Burst Count 更新逻辑-----------------------------------
always@(posedge CLKi or posedge RSTi)begin
    if(RSTi)begin
        Burst_cnt <= 'd0;
    end
    else if(FIB_FSM==FIB_IDLE)begin
        Burst_cnt <= 'd0;
    end
    else if((FIB_FSM==FIB_SEQ_READ)|(FIB_FSM==FIB_SEQ_WRITE))begin
        Burst_cnt <= Burst_cnt + 'd1;
    end
end
//------------------------SRAM port 以及产生回执-------------------------------------------
always@(*)begin
    case(FIB_FSM)
            FIB_SIG_READ : begin
                            SRAM_WREN  = 1'b0;
                            SRAM_BSEL  = Byte_sel;
                            SRAM_DATAi = FIB_AQ_DATA; 
                            SRAM_ADDR  = FIB_AQ_ADDR[`XLEN-1:3];
                            VFIBSo_V   = 1'b1;
                            VFIBSo_ID  = FIB_AQ_ID;
                            VFIBSo_RPL = `FIB_RPL_TRDY;
                            VFIBSo_DATA= SRAM_DATAo;
                           end
            FIB_SIG_WRITE: begin
                            SRAM_WREN  = 1'b1;
                            SRAM_BSEL  = Byte_sel;
                            SRAM_DATAi = FIB_AQ_DATA; 
                            SRAM_ADDR  = FIB_AQ_ADDR[`XLEN-1:3];
                            VFIBSo_V   = 1'b1;
                            VFIBSo_ID  = FIB_AQ_ID;
                            VFIBSo_RPL = `FIB_RPL_TRDY;
                            VFIBSo_DATA= 64'hx;
                           end
            FIB_SEQ_READ : begin
                            SRAM_WREN  = 1'b0;
                            SRAM_BSEL  = 8'b1111_1111;
                            SRAM_DATAi = FIB_AQ_DATA; 
                            SRAM_ADDR  = FIB_AQ_ADDR[`XLEN-1:3] + Burst_cnt;
                            VFIBSo_V   = 1'b1;
                            VFIBSo_ID  = FIB_AQ_ID;
                            VFIBSo_RPL = (Burst_cnt=='d7) ? `FIB_RPL_TRDY : `FIB_RPL_SEQ;
                            VFIBSo_DATA= SRAM_DATAo;
                           end
            FIB_SEQ_WRITE: begin
                            SRAM_WREN  = 1'b1;
                            SRAM_BSEL  = 8'b1111_1111;                          //所有字节全部选中
                            SRAM_DATAi = FIB_AQ_DATA; 
                            SRAM_ADDR  = FIB_AQ_ADDR[`XLEN-1:3] + Burst_cnt;
                            VFIBSo_V   = (Burst_cnt=='d7) ? 1'b1 : 1'b0;
                            VFIBSo_ID  = FIB_AQ_ID;
                            VFIBSo_RPL = (Burst_cnt=='d7) ? `FIB_RPL_TRDY : `FIB_RPL_SEQ;
                            VFIBSo_DATA= SRAM_DATAo;
                           end
            default      : begin
                            SRAM_WREN  = 1'b0;
                            SRAM_BSEL  = 8'hxx;
                            SRAM_DATAi = 64'hx; 
                            SRAM_ADDR  = 64'hx;
                            VFIBSo_V   = 1'b0;
                            VFIBSo_ID  = 8'hx;
                            VFIBSo_RPL = 8'hx;
                            VFIBSo_DATA= 'hx;
                           end
    endcase
end
assign FIB_AQ_ACk = (FIB_FSM == FIB_READ_NEW) | (FIB_FSM==FIB_SEQW_NEXT);

endmodule

module DataShiftL(
    input wire [3:0]    Offset_ADDR,
    input wire [63:0]   DATAi,
    input wire [3:0]    SIZEi,
    output reg          MisAligned,         //当前访问不对齐
    output wire [15:0]  BSELo,
    output wire [127:0] DATAo
);
    wire [127:0] shift0, shift1, shift2;
    wire [15:0]  bsel_base, bsel0, bsel1, bsel2;
//---------------------生成移位后的数据----------------------
assign shift0 = Offset_ADDR[0] ? {56'b0,DATAi,8'b0} : {64'b0, DATAi};
assign shift1 = Offset_ADDR[1] ? {shift0[111:0],16'b0} : shift0;
assign shift2 = Offset_ADDR[2] ? {shift1[95:0],32'b0} : shift1;
assign DATAo  = Offset_ADDR[3] ? {shift2[63:0],64'b0} : shift2;
//------------------生成字节掩码-----------------------------
assign bsel_base =  (SIZEi[0] ? 16'b00000000_00000001 : 16'b00000000_00000000) |
                    (SIZEi[1] ? 16'b00000000_00000011 : 16'b00000000_00000000) |
                    (SIZEi[2] ? 16'b00000000_00001111 : 16'b00000000_00000000) |
                    (SIZEi[3] ? 16'b00000000_11111111 : 16'b00000000_00000000);     //
assign bsel0 = Offset_ADDR[0] ? {bsel_base[14:0],1'b0} : bsel_base;
assign bsel1 = Offset_ADDR[1] ? {bsel0[13:0],2'b0} : bsel0;
assign bsel2 = Offset_ADDR[2] ? {bsel1[11:0],4'b0} : bsel1;
assign BSELo = Offset_ADDR[3] ? {bsel2[7:0],8'b0} : bsel2;
//产生不对齐信号
always@(*)begin
    case(SIZEi)
        4'h1 : MisAligned = 1'b0; 
        4'h2 : MisAligned = (Offset_ADDR[0] != 1'b0);
        4'h4 : MisAligned = (Offset_ADDR[1:0] != 2'b00);
        4'h8 : MisAligned = (Offset_ADDR[2:0] != 3'b000);
    default  : MisAligned = 1'b0;
    endcase
end

endmodule